package utils

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"math/big"
	"os"
	"regexp"
	"sort"
	"strings"
)

const (
	VIStrSafe    = "safestr"
	VIStrNumber  = "number"
	VIStrNoSpace = "noSpace"
)

//VerifyInput 验证输入的参数
func VerifyInput(str string, filter string) bool {
	var reg *regexp.Regexp
	switch filter {
	case "nickname":
		reg = regexp.MustCompile(`^[a-z0-9A-Z\p{Han}]+[_\-a-z0-9A-Z\p{Han}]*$`)
	case "username":
		reg = regexp.MustCompile(`^[a-zA-Z]+[_\-\.a-z0-9A-Z]*$`)
	case "email":
		reg = regexp.MustCompile(`^[\w\_\-\.]+@[\w\-\.]+$`)
	case "ip":
		reg = regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`)
	case "ipport":
		reg = regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:?\d*$`)
	case VIStrNumber:
		reg = regexp.MustCompile(`^-?\d+$`)
	case "mobile":
		reg = regexp.MustCompile(`^0?1[0|3|4|5|7|8][0-9]\d{8}$`)
	case "port":
		reg = regexp.MustCompile(`^(,?\w*/?(tcp|udp)/\d+)+$`)
	case "scriptname":
		reg = regexp.MustCompile(`^[a-zA-Z][\w|\.|-]{2,}\.(sh|py|pl)$`)
	case "path":
		reg = regexp.MustCompile(`^[_\.\-a-z0-9A-Z\/]+$`)
	case "bytes":
		reg = regexp.MustCompile(`^[0-9A-Fa-f]+$`)
	case "argv":
		reg = regexp.MustCompile(`^(\-\-?[a-zA-Z]+\s+[a-zA-Z0-9\.\-\_]+\s*)+$`)
	case "id":
		reg = regexp.MustCompile(`^[a-f0-9]{24}$`)
	case "md5":
		reg = regexp.MustCompile(`^[a-f0-9]{32}$`)
	case "ver":
		reg = regexp.MustCompile(`^[a-z0-9A-Z\.]+$`)
	case VIStrSafe:
		reg = regexp.MustCompile(`^[a-z0-9A-Z\.\-\\/_,]+$`)
	case VIStrNoSpace:
		reg = regexp.MustCompile(`^\S+$`)
	case "zh_cn":
		reg = regexp.MustCompile("^[\u4e00-\u9fa5]+$")
	default:
		return true
	}
	return reg.MatchString(str)
}

//Sha256 sha256加密
func Sha256(data []byte, resultType string) (sec string) {
	checksum := sha256.Sum256(data)
	switch resultType {
	case "base64":
		encodeBase64 := base64.StdEncoding
		data := make([]byte, sha256.Size)
		copy(data, checksum[:sha256.Size])
		sec = encodeBase64.EncodeToString(data)
	case "hex":
		sec = fmt.Sprintf("%02x", checksum)
	default:
		sec = fmt.Sprintf("%02x", checksum)
	}
	return
}

func MD5(data []byte, resultType string) (sec string) {
	m := md5.Sum(data)
	switch resultType {
	case "base64":
		encodeBase64 := base64.StdEncoding
		var data []byte
		copy(data, m[:md5.Size])
		sec = encodeBase64.EncodeToString(data)
	case "hex":
		sec = fmt.Sprintf("%02x", m)
	}
	return
}

//SearchString 从字符串数组中查找字符串
func SearchString(a []string, s string, sorted bool) bool {
	alen := len(a)
	if alen == 0 {
		return false
	}
	if sorted {
		i := sort.SearchStrings(a, s)
		if i >= 0 && i < alen && a[i] == s {
			return true
		}
	} else {
		for _, v := range a {
			if v == s {
				return true
			}
		}
	}
	return false
}

// FileMd5 获取文件的md5值。
func FileMd5(fileName string) ([]byte, error) {
	file, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	h := md5.New()
	if _, err := io.Copy(h, file); err != nil {
		return nil, err
	}
	return h.Sum(nil), nil
}

/*
  RandomStr 48-57:数字 65-90:大写字母 97-122:小写字母 10+26+26=62
  参数：
  l： 产生的随机字符串的长度
  sc：是否包含特殊字符
*/
func RandomStr(l int, sc bool) []byte {
	b := make([]byte, l)
	for i := 0; i < l; i++ {
		var n uint8
		rnum := Random(int64(1000))
		if sc {
			n = uint8(rnum%94 + 32)
		} else {
			n = uint8(rnum%62 + 48)
			if n > 57 && n < 84 {
				n += 7
			} else if n >= 84 {
				n += 13
			}
		}
		// 确保不会有重复的字符
		if bytes.IndexByte(b, n) == -1 {
			b[i] = n
		} else {
			i--
		}
	}
	return b
}

// Random 有符号的随机数
func Random(max int64) int {
	bi, _ := rand.Int(rand.Reader, big.NewInt(max))
	return int(bi.Uint64())
}

// Urandom 无符号的随机数
func Urandom() uint32 {
	vals := make([]byte, 4)
	rand.Reader.Read(vals)
	var i uint32
	for k := range vals {
		i = i | uint32(uint32(vals[k])<<((8-k-1)*8))
	}
	return i
}

//整形转换成字节
func IntToBytes(n int) []byte {
	x := int32(n)
	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形
func BytesToInt(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int32
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

func BuilderConcat(strs ...string) (strBuilder *strings.Builder) {
	strBuilder = &strings.Builder{}
	sep := strs[0]
	sepLen := len(sep)
	strsLen := len(strs)
	for i := 1; i < strsLen; i++ {
		strBuilder.Grow(len(strs[i]) + sepLen)
		strBuilder.WriteString(strs[i])

		if i < (strsLen - 1) {
			strBuilder.WriteString(sep)
		}
	}
	return
}

// Round 返回指定的小数精度
func Round(val float64, precision int) float64 {
	p := math.Pow10(precision)
	return math.Floor(val*p+0.5) / p
}
